﻿using System.Collections.Generic;
using UnityEngine;

namespace CWM.Skinn
{

    public static partial class SkinnEx
    {
        public static List<Blendshape> GetBlendshapes(this Mesh sourceMesh)
        {
            var blendShapeCount = sourceMesh.blendShapeCount;
            if (blendShapeCount < 1) new List<Blendshape>();
            var shapes = new List<Blendshape>();
            var vertexCount = sourceMesh.vertices.Length;
            for (int i = 0; i < blendShapeCount; i++)
            {
                var shapeName = sourceMesh.GetBlendShapeName(i);
                var frameCount = sourceMesh.GetBlendShapeFrameCount(i);
                var blendshape = new Blendshape() { name = shapeName };

                for (int ii = 0; ii < frameCount; ii++)
                {
                    var frameWeight = sourceMesh.GetBlendShapeFrameWeight(i, ii);

                    var sourceDeltas = new Vector3[sourceMesh.vertices.Length];
                    var sourceNormals = new Vector3[sourceMesh.vertices.Length];
                    var sourceTangents = new Vector3[sourceMesh.vertices.Length];

                    sourceMesh.GetBlendShapeFrameVertices(i, ii, sourceDeltas, sourceNormals, sourceTangents);

                    if (IsNullOrEmpty(sourceDeltas, vertexCount)) sourceDeltas = null;
                    if (IsNullOrEmpty(sourceNormals, vertexCount)) sourceNormals = null;
                    if (IsNullOrEmpty(sourceTangents, vertexCount)) sourceTangents = null;

                    blendshape.AddFrame(frameWeight, sourceDeltas, sourceNormals, sourceTangents);
                }
                if (blendshape.frames.Count > 0) shapes.Add(blendshape);
            }
            return shapes;
        }

        public static void ClearUnusedBlendshapes(this Mesh mesh)
        {
            ClearUnusedBlendshapes(mesh, 0.01f.ToUnitFromStandard(ConversionUnits.Inch));
        }

        public static void ClearUnusedBlendshapes(this Mesh mesh, float minDelta)
        {
            if (!mesh) return;
            var minMag = minDelta;
            var shapes = mesh.GetBlendshapes();
            mesh.ClearBlendShapes();
            foreach (Blendshape shape in shapes)
            {
                var mag = 0f;

                for (int i = 0; i < shape.frames.Count; i++)
                {
                    var deltas = shape.frames[i].shapeDeltas;
                    var shapeNormals = shape.frames[i].shapeNormals;
                    var shapeTangents = shape.frames[i].shapeTangents;

                    if (IsNullOrEmpty(deltas)) continue;

                    var hasNormals = !IsNullOrEmpty(shapeNormals);
                    var hasTangents = !IsNullOrEmpty(shapeTangents);

                    for (int ii = 0; ii < deltas.Length; ii++)
                    {
                        if (mag >= minMag) { mag += 1f; break; }

                        mag += deltas[ii].magnitude;
                   
                        if (hasNormals) mag += shapeNormals[ii].magnitude;
                        if (hasTangents) mag += shapeTangents[ii].magnitude;
                    }

                    if (mag >= minMag)  {  mag += 1f;  break; }
                }

                if (mag >= minMag)
                {
                    for (int i = 0; i < shape.frames.Count; i++)
                    {

                        mesh.AddBlendShapeFrame(shape.name,
                            shape.frames[i].frameWeight,
                            shape.frames[i].shapeDeltas,
                            shape.frames[i].shapeNormals,
                            shape.frames[i].shapeTangents
                            );
                    }
                }
            }
        }
    }
}